package com.cirkana.viewer.data.security;

import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import com.cirkana.ArrayUtil;
import com.cirkana.data.DataException;
import com.cirkana.data.IDataCriteria;
import com.cirkana.data.IDataProvider;
import com.cirkana.data.IDataRecord;
import com.cirkana.data.security.ISecurityDataResolver;
import com.cirkana.data.security.IUser;
import com.cirkana.data.security.IUserService;
import com.cirkana.viewer.data.criteria.DataCriteria;

public class SecurityDataResolver implements ISecurityDataResolver {

	private IDataProvider dataProvider;

	public SecurityDataResolver(IDataProvider dataProvider) {
		this.dataProvider = dataProvider;
	}

	@Override
	public IUser newUser() {
		return new User();
	}

	@Override
	public IUser newUser(String nickName, String name, String middleName, String lastName, String email, Date birthDate, String city, String country,
			String language, Integer avatarPicId, String avatarSummary, Map<String, IUserService> userServices) {
		User user = new User();
		user.setNickName(nickName);
		user.setName(name);
		user.setMiddleName(middleName);
		user.setLastName(lastName);
		user.setEmail(email);
		user.setBirthDate(birthDate);
		user.setCity(city);
		user.setCountry(country);
		user.setLanguage(language);
		user.setAvatarPicId(avatarPicId);
		user.setAvatarSummary(avatarSummary);
		user.setUserServices(userServices);
		return user;
	}

	@Override
	public IUser getUser(String nickName, String password) throws DataException {
		ArrayList<IDataCriteria> criterion = DataCriteria.getDataCriteriaList(DataCriteria.getStringEqualsDataCriteria(User.COLUMN_NICK_NAME, nickName),
				DataCriteria.getStringEqualsDataCriteria(User.COLUMN_PASSWORD, password));
		IUser user = (IUser) this.dataProvider.select(User.tableDef, criterion).get(0);
		user.setUserServices(getUserServices(user.getId()));
		return user;
	}

	protected Map<String, IUserService> getUserServices(Integer userId) throws DataException {
		ArrayList<IDataCriteria> criterion = DataCriteria.getDataCriteriaList(DataCriteria.getIdDataCriteria(UserService.COLUMN_USER_ID, userId));
		ArrayList<IDataRecord> records = this.dataProvider.select(UserService.tableDef, criterion);
		Map<String, IUserService> table = new Hashtable<String, IUserService>();
		for (IDataRecord record : records)
			table.put(((UserService) record).getServiceName(), (IUserService) record);
		return table;
	}

	@Override
	public IUser insertUser(IUser user) throws DataException {
		this.dataProvider.begin();
		try {
			Integer id = this.dataProvider.insert(User.tableDef, (IDataRecord) user);
			user.setId(id);
			for (String serviceName : user.getUserServices().keySet())
				insertUserService(user, serviceName, user.getUserServices().get(serviceName));
			this.dataProvider.commit();
			return user;
		} catch (DataException e) {
			this.dataProvider.rollback();
			throw e;
		} catch (Exception e) {
			this.dataProvider.rollback();
			throw new DataException(e);
		}
	}

	@Override
	public IUser updateUser(IUser user) throws DataException {
		this.dataProvider.begin();
		try {
			this.dataProvider.update(User.tableDef, (IDataRecord) user);
			updateUserServices(user, user.getUserServices());
			this.dataProvider.commit();
			return user;
		} catch (DataException e) {
			this.dataProvider.rollback();
			throw e;
		} catch (Exception e) {
			this.dataProvider.rollback();
			throw new DataException(e);
		}
	}

	@Override
	public void deleteUser(IUser user) throws DataException {
		this.dataProvider.begin();
		try {
			for (String serviceName : user.getUserServices().keySet())
				deleteUserService(user.getUserServices().get(serviceName));
			this.dataProvider.delete(User.tableDef, (IDataRecord) user);
			this.dataProvider.commit();
		} catch (DataException e) {
			this.dataProvider.rollback();
			throw e;
		} catch (Exception e) {
			this.dataProvider.rollback();
			throw new DataException(e);
		}
	}

	protected void updateUserServices(IUser user, Map<String, IUserService> userServices) throws DataException {
		Map<String, IUserService> old = getUserServices(user.getId());
		List<String> newKeys = ArrayUtil.sort(userServices.keySet());
		List<String> oldKeys = ArrayUtil.sort(old.keySet());
		for (String key : newKeys)
			if (oldKeys.contains(key))
				updateUserService(userServices.get(key));
			else
				insertUserService(user, key, userServices.get(key));
		for (String key : oldKeys)
			if (!newKeys.contains(key))
				deleteUserService(old.get(key));
	}

	protected void insertUserService(IUser user, String serviceName, IUserService userService) throws DataException {
		((UserService) userService).setUserId(user.getId());
		((UserService) userService).setServiceName(serviceName);
		this.dataProvider.insert(UserService.tableDef, (IDataRecord) userService);
	}

	protected void updateUserService(IUserService userService) throws DataException {
		this.dataProvider.update(UserService.tableDef, (IDataRecord) userService);
	}

	protected void deleteUserService(IUserService userService) throws DataException {
		this.dataProvider.delete(UserService.tableDef, (IDataRecord) userService);
	}

}
